package com.example.demo.mqclient;

import com.example.demo.common.*;
import com.sun.java.swing.plaf.windows.WindowsTabbedPaneUI;

import javax.websocket.RemoteEndpoint;
import java.io.*;
import java.net.Socket;
import java.net.SocketException;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 张
 * Date: 2024-08-24
 * Time: 3:03
 */
public class Connection {
    private Socket socket = null;
    //需要管理多个Channel,使用hash表把若干个channel组织起来
    //key 为 channelId ,value 为 Channel对象
    private ConcurrentHashMap<String,Channel> channelMap = new ConcurrentHashMap<>();

    private InputStream inputStream;
    private OutputStream outputStream;
    private DataInputStream dataInputStream;
    private DataOutputStream dataOutputStream;
    //创建一个线程池来进行回调(和扫描线程分开)
    private ExecutorService callbackPool = null;

    public Connection(String host,int port) throws IOException {
        socket = new Socket(host,port);
        inputStream = socket.getInputStream();
        outputStream = socket.getOutputStream();
        dataInputStream = new DataInputStream(inputStream);
        dataOutputStream = new DataOutputStream(outputStream);
        callbackPool = Executors.newFixedThreadPool(4);

        //创建一个扫描线程,从socket中读取响应数据,在把响应数据交给对应的Channel进行处理
        Thread thread = new Thread(() ->{
            try{
                while (!socket.isClosed()) {
                    Response response = readResponse();
                    //此处的响应有两种情况 1.用户完成某个操作服务器返回结果 2.服务器推送消息
                    dispatchResponse(response);
                }
            } catch (SocketException e) {
                //连接正常断开,这个异常直接忽略
                System.out.println("[Connection] 连接正常断开!!");
            } catch (IOException | ClassNotFoundException | MqException e) {
                System.out.println("[Connection] 连接异常断开!!");
                e.printStackTrace();
            }
        });
        thread.start();
    }

    //使用这个方法来处理响应的不同情况
    private void dispatchResponse(Response response) throws IOException, ClassNotFoundException, MqException {
        if (response.getType() == 0xc) {
            //服务器推送的消息数据
            SubScribeReturns subScribeReturns = (SubScribeReturns) BinaryTool.fromBytes(response.getPayload());
            //根据ChannelId找到对应的channel对象
            Channel channel = channelMap.get(subScribeReturns.getChannelId());
            if (channel == null) {
                throw new MqException("[Connection] 该消息对应的channel在客户端中不存在!! channelId = " + subScribeReturns.getChannelId());
            }
            //执行对应的回调 交给专门的线程池来进行处理
            callbackPool.submit(() -> {
                try {
                    channel.getConsumer().handleDelivery(subScribeReturns.getConsumerTag(),subScribeReturns.getBasicProperties(), subScribeReturns.getBody());
                } catch (IOException | MqException e) {
                    e.printStackTrace();
                }
            });
        } else {
            //当前响应为控制请求的响应
            BasicReturn basicReturn = (BasicReturn) BinaryTool.fromBytes(response.getPayload());
            //把这个结果个放到对应channel的hash表中,channel里面还有个阻塞队列在等这个
            Channel channel = channelMap.get(basicReturn.getChannelId());
            if(channel == null) {
                throw new MqException("[Connection] 该消息对应的channel在客户端中不存在!! channelId = " + basicReturn.getChannelId());
            }
            System.out.println("[Connection] basicReturn = " + basicReturn.toString());
            channel.putReturn(basicReturn);
        }
    }

    //关闭Connection,释放上述资源
    public void close() {
        try{
            callbackPool.shutdownNow();
            channelMap.clear();
            inputStream.close();
            outputStream.close();//dataInputStream和dataOutputStream也会随之关闭
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //发送请求
    public void writeRequest(Request request) throws IOException {
        dataOutputStream.writeInt(request.getType());
        dataOutputStream.writeInt(request.getLength());
        dataOutputStream.write(request.getPayload());
        dataOutputStream.flush();
        System.out.println("[Connection] 发送请求!! type = " + request.getType() + ", length = " + request.getLength());
    }

    //读取响应
    public Response readResponse() throws IOException {
        Response response = new Response();
        response.setType(dataInputStream.readInt());
        response.setLength(dataInputStream.readInt());
        byte[] payload = new byte[response.getLength()];
        int n = dataInputStream.read(payload);
        if(n != response.getLength()) {
            throw new IOException("读取的响应数据不完整");
        }
        response.setPayload(payload);
        return response;
    }

    //创建一个Channel
    public Channel createChannel() throws IOException {
        String channelId = "C-" + UUID.randomUUID().toString();
        Channel channel = new Channel(channelId,this);
        //把channel对象管理起来
        channelMap.put(channelId,channel);
        //通知也需要把创建Channel的这个消息给服务器知道 0x1创建channel
        boolean ok = channel.createChannel();//channel内部实现
        if(!ok) {
            //服务器中channel创建失败!! 整个这次创建Channel操作不顺利!!
            //此时就认为创建Channel失败,把hash表中的键值对给删了
            channelMap.remove(channelId);
            return null;
        }
        return channel;
    }


}
